/*********************************************************************************
* Copyright (c) zhujinyang.
* All rights reserved.
*
* \file
*
* os shell
*
* 
*********************************************************************************/
#include "os_ext_api.h"
#include "cc_error.h"
#include "string.h"
#include "hal_uart_drv.h"
#include "mal_printf.h"





/*
********************************************************************************
* define config parameters
********************************************************************************
*/
#define ACCOUNT_NAME   "zjy"
#define PASSWORD       "1"


/*
********************************************************************************
* define struct
********************************************************************************
*/
typedef struct {
    INT8U               nreg;
    INT8U               phase;
    char                username[30];
} SHELL_CB_T;

typedef struct {
    char *username;
    char *password;
} SHELL_ACCOUNT_T;

typedef struct shell_cmd {
    const  char      *cmd;
    void            (*handler)(const char *cmd, int argc, char *argv[]);
} SHELL_CMD_ENTRY_T;



static void shell_help(const char *cmd, int argc, char *argv[]);

/*
********************************************************************************
* define module variants
********************************************************************************
*/
static    SHELL_CB_T            s_scb;
static    const SHELL_ACCOUNT_T s_shell_account[] = {{ACCOUNT_NAME, PASSWORD},     };
static    SHELL_CMD_ENTRY_T     s_shell_tbl[20];
static    const SHELL_CMD_ENTRY_T s_shell_help = {"help", shell_help};
                   





INT32S printf_com_ex(const char *str, ...);
/**********************************************************************************
* \brief:       help cmd process
* \param[in]:   cmd:      string
* \param[in]:   argc:   num of parameter
* \param[in]:   argv:   parameter pointer
* \return:      none
**********************************************************************************/
static void shell_help(const char *cmd, int argc, char *argv[])
{
    printf_com_ex("usage: cmd param1 param2 ... paramN\r\n");
}

/**********************************************************************************
* \brief:       Determine if it is a valid character
* \param[in]:   ch:      input char
* \return:      true : valid, false : invalid
**********************************************************************************/
static BOOLEAN shell_validchar(char ch)
{
    if (ch >= 'a' && ch <= 'z') {
        return true;
    }
    
    if (ch >= 'A' && ch <= 'Z') {
        return true;
    }

    if (ch >= '0' && ch <= '9') {
        return true;
    }

    switch (ch) {
    case ' ':
    case '_':
    case '-':
    case '.':
        return true;

    default :
        return false;
    }
}





/**********************************************************************************
* \brief:       compare with the cmd and process cmd
* \param[in]:   cmd:    cmd string
* \param[in]:   argc:   num of parameter
* \param[in]:   argv:   parameter pointer
* \return:      none
**********************************************************************************/
static void shell_process(char *cmd, int argc, char *argv[])
{
    INT32U  i;


    switch (s_scb.phase) {
    case 0:   // username
        if (argc == 0 && strlen(cmd) < sizeof(s_scb.username) - 1) {
            memset(s_scb.username, 0,   sizeof(s_scb.username));
            memcpy(s_scb.username, cmd, strlen(cmd));
            s_scb.phase = 1;
        } else {
            printf_com_ex("invalid username!\r\n");
        }
        return;

    case 1:  //password
        if (argc == 0) {
            for (i = 0; i < sizeof(s_shell_account)/sizeof(s_shell_account[0]); i++) {
                if (strcmp(s_scb.username, s_shell_account[i].username) == 0
                &&  strcmp(cmd, s_shell_account[i].password) == 0) {
                    s_scb.phase = 2;
                    printf_com_ex("welcome to used os shell!\r\n");
                    return;
                }
            }
        }
        s_scb.phase = 0;
        printf_com_ex("invalid account!\r\n");
        return;

    default:
        for (i = 0; i < s_scb.nreg; i++) {
            if (s_shell_tbl[i].cmd != 0 && strcmp(cmd, s_shell_tbl[i].cmd) == 0) {
                s_shell_tbl[i].handler(cmd, argc, argv);
                return;
            }
        }
        if (s_shell_help.cmd != 0 && strcmp(cmd, s_shell_help.cmd) == 0) {
            s_shell_help.handler(cmd, argc, argv);
            return;
        }

        printf_com_ex("unknow cmd\r\n");
        return;
    }
}




/**********************************************************************************
* \brief:       console entry
* \param[in]:   pdata  : task private data
* \return:      none
**********************************************************************************/
static void console_entry(void *argv)
{
    static char    input[50];
    static INT8U   len = 0;
    char           ch;
    INT32S         ret;
    
    printf_com_ex("login: ");
    
    for (; ;) {
        os_thread_sleep(50);

        for (; ; ) {
            if ((ret = hal_uart_read_byte(UART_COM_1)) == -1) {
                break;
            }
    
            ch = ret;
            if (ch == '\r' || ch == '\n') {
                char             *pos,  *argv[10];
                INT32U            argc = 0;
                const char *const prompt[] = {"\r\nlogin: ", "\r\npsswd: ", "\r\nENE>"};
                
                input[len++] = '\0';
                for (pos = input; pos < &input[len]; ) {
                    if (*pos != '\0') {
                        argv[argc++] = pos;
                        if (argc >= sizeof(argv)/sizeof(argv[0])) {
                            printf_com_ex("argc exceed!\r\n");
                            goto error_exit;
                        }
                        pos += strlen(pos);
                    } else {
                        pos++;
                    }
                }
    
                if (argc > 0) {
                    printf_com_ex("\r\n");
                    shell_process(argv[0], argc - 1, &argv[1]);
                }
                
                error_exit:
                len  = 0;
                CC_ASSERT(s_scb.phase <= 2);
                printf_com_ex(prompt[s_scb.phase]);
            } else if (ch == '\b') {
                if (len > 0) {
                    printf_com_ex("\b \b");
                    input[--len] = '\0';
                }
            } else if (shell_validchar(ch) == true) {
                if (len >= sizeof(input) - 1) {
                    len = 0;
                    printf_com_ex("cmd too long!\r\n");
                } else {
                    char tmpchar[2];
                    
                    input[len++] = (ch == ' ')? 0 : ch;
                    tmpchar[0] = ch;
                    tmpchar[1] = 0;
                    printf_com_ex(tmpchar);
                }
            }
        }
    }
}




/**********************************************************************************
* \brief:       console register handler
* \param[in]:   cmd: Command string, note that it must be a string pointer, example: "help"  
* \param[in]:   handler: command handler function
* \return:      none
**********************************************************************************/
void mal_console_register_handler(const char *cmd, void (*handler)(const char *cmd, int argc, char *argv[]))
{
    INT8U  i, nreg;
    
    CC_ASSERT(cmd != 0 && handler != 0);
    for (i = 0; i < s_scb.nreg; i++) {
        CC_ASSERT(strcmp(cmd, s_shell_tbl[i].cmd) != 0);
    }
    
    nreg = s_scb.nreg++;
    CC_ASSERT(nreg < sizeof(s_shell_tbl) / sizeof(s_shell_tbl[0]));
    s_shell_tbl[nreg].cmd     = cmd;
    s_shell_tbl[nreg].handler = handler;
}


/**********************************************************************************
* \brief:       console init
* \param[in]:   none  
* \return:      none
**********************************************************************************/
void mal_console_init(void)
{
    void  *tid;
    
    memset(&s_scb, 0, sizeof(s_scb));
    
    tid = os_thread_create(-1, console_entry, 0, (void *)0, 1, 0);
    CC_ASSERT(tid);
}




